概述
Web 推送通知允許用戶在Web 應用程序中選擇接收更新信息,這些旨在重新吸引用戶群注意的更新信息通常是對用戶來說有趣、重要、實時的內容。
推送基於我們在上一篇文章中詳細討論過的Service Worker。
在這種情況下,使用Service Worker 的原因是它們在後台工作。這對推送通知非常有用,因為這意味著只有當用戶與通知本身進行交互時才會執行它們的代碼。
推送和通知
推送和通知是兩種不同的API。
- 推送 ——它在服務器端將消息推送給Service Worker時被調用
- 通知 ——這是Service Worker或web應用程序中向用戶顯示信息腳本的操作。
推送
實現推送有三個步驟:
- UI ——添加必要的客戶端邏輯來讓用戶訂閱推送,這是你的Web應用程序UI需要的JavaScript邏輯,這樣用戶就能給自己註冊從而可以收到消息推送。
- 發送推送消息 ——在服務器上實現API調用,該調用將觸發對用戶設備的推送消息。
- 接受推送消息 ——一旦推送消息到達瀏覽器,就進行處理。
現在我們將更詳細地描述整個過程。
瀏覽器支持檢測
首先,我們需要檢查當前瀏覽器是否支持推送消息。我們可以通過兩個簡單的方法檢查是否支持推送消息:
- 檢查
navigator
對像上的serviceWorker
- 檢查
window
對像上的PushManager
兩種檢查看起來都是這樣的:
if (!('serviceWorker' in navigator)) {
// Service Worker isn't supported on this browser, disable or hide UI.
return;
}
if (!('PushManager' in window)) {
// Push isn't supported on this browser, disable or hide UI.
return;
}
复制代码
註冊一個Service Worker
此時,我們知道該功能是受支持的。下一步是註冊我們的Service Worker。
如何註冊Service Worker,你從我們以前的一篇文章中應該已經熟悉了。
請求許可
在註冊了Service Worker 之後,我們可以開始訂閱用戶。要做到這一點,我們需要得到他的許可才能給他發送推送信息。
獲取許可的API相對簡單,但缺點是API已經從接受回調變為返回Promise,這帶來了一個問題:我們無法判斷當前瀏覽器實現了哪個API版本,因此你必須實現和處理這兩個版本。
看起來是這樣的:
function requestPermission() {
return new Promise(function(resolve, reject) {
const permissionResult = Notification.requestPermission(function(result) {
// Handling deprecated version with callback.
resolve(result);
});
if (permissionResult) {
permissionResult.then(resolve, reject);
}
})
.then(function(permissionResult) {
if (permissionResult !== 'granted') {
throw new Error('Permission not granted.');
}
});
}
复制代码
Notification.requestPermission()
調用將向用戶顯示以下提示:
一旦被授權、關閉或阻止,我們將得到字符串格式的結果:‘granted’
、‘default’
或‘denied’
。
記住,如果用戶單擊Block
按鈕,你的Web應用程序將無法再次請求用戶的許可,直到他們通過更改權限狀態手動“unblock”你的應用程序的限制。此選項隱藏在設置界面中。
用戶訂閱使用PushManager
一旦我們註冊了Service Worker並獲得許可權限,當你在註冊你的Service Worker時,我們就可以通過調用registration.pushManager.subscribe()
來訂閱用戶。
整個片段可能如下所示(包括Service Workder 註冊):
function subscribeUserToPush() {
return navigator.serviceWorker.register('service-worker.js')
.then(function(registration) {
var subscribeOptions = {
userVisibleOnly: true,
applicationServerKey: btoa(
'BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U'
)
};
return registration.pushManager.subscribe(subscribeOptions);
})
.then(function(pushSubscription) {
console.log('PushSubscription: ', JSON.stringify(pushSubscription));
return pushSubscription;
});
}
复制代码
registration.pushManager.subscribe(options)
接受一個options對象,它包含必要參數和可選參數:
- userVisibleOnly:布爾值指示返回的推送訂閱將僅用於對用戶可見的消息。它必須設置為
true
,否則你會得到一個錯誤(這有歷史原因)。 - applicationServerKey:Base64-encoded
DOMString
或者ArrayBuffer
包含推送服務器用來驗證應用服務器的公鑰。
你的服務器需要生成一對應用程序服務器密鑰 ——也稱為VAPID密鑰,對於你的服務器來說,它們是唯一的。它們是一對公鑰和私鑰。私鑰被秘密存儲在你的終端,而公鑰則與客戶端交換。這些密鑰允許推送服務知道哪個應用服務器訂閱了用戶,並確保它是觸發向該特定用戶推送消息的相同服務器。
你只需要為應用程序創建一次私鑰/公鑰對。做到這一點的方法是去完成這個—— web-push-codelab.glitch.me/。
瀏覽器在訂閱用戶時將applicationServerKey
(公鑰)傳遞給推送服務,這意味著推送服務可以將應用程序的公鑰綁定到用戶的PushSubscription
中。
情況是這樣的:
- 你的web app被加載時,你可以調用
subscribe()
來傳入你的server密鑰。 - 瀏覽器向生成端點的推送服務發出請求,將此端點與該鍵關聯並將端點返回給瀏覽器。
- 瀏覽器將此端點添加到
PushSubscription
對像中,該對象通過subscribe()
的promise返回。
之後,無論你想何時發送推送消息,你都需要創建一個包含使用應用程序服務器的專用密鑰簽名信息的Authorization header。當推送服務收到發送推送消息的請求時,它將通過查找已經連接到該特定端點的公鑰來驗證頭(第二步)。
推送對象
PushSubscription
包含用戶設備發送推送消息所需的所有信息。就像這樣:
{
"endpoint": "https://domain.pushservice.com/some-id",
"keys": {
"p256dh":
"BIPUL12DLfytvTajnryr3PJdAgXS3HGMlLqndGcJGabyhHheJYlNGCeXl1dn18gSJ1WArAPIxr4gK0_dQds4yiI=",
"auth":"FPssMOQPmLmXWmdSTdbKVw=="
}
}
复制代码
endpoint
是推送服務的URL。要觸發推送消息,請對此URL 發送POST 請求。
這裡的keys
對象的值是用來加密推送消息帶過來的消息數據。
一旦用戶被訂閱並且你有PushSubscription
,你需要將它發送到你的服務器。在那裡(在服務器上),你將這個訂閱存到數據庫中,從今以後如果你要向該用戶推送消息就使用它。
發送推送消息
當你想向用戶發送推送消息時,你首先需要一個推送服務。你要(通過API 調用)告訴推送服務要發送哪些數據,誰來接收數據以及其他關於怎麼發送數據的標準。通常,此API 調用是在你服務器上完成的。
推送服務
推送服務是接收請求,驗證請求並將推送消息傳遞給對應的瀏覽器。
注意推送服務不是由你管理的——它是第三方服務。你的服務器通過API與推送服務進行通訊。推送服務的一個例子是Google的FCM。
推送服務處理所有繁重的任務,比如,如果瀏覽器處於脫機狀態,推送服務會在發送相應消息之前對消息進行排隊,等待瀏覽器的再次聯機。
每個瀏覽器都使用它們想要的任何推送服務,這是開發者無法控制的。
然而所有的推送服務都具有相同的API,因此在實現過程中不會有很大難度。
為了獲得URL來進行消息推送請求,你需要檢查PushSubscription
對像中存儲的endpoint
值。
推送服務API
推送服務API提供了一種將消息發送給用戶的方式。API基於 Web推送協議,它是一種定義瞭如何對推送服務進行API調用的IETF標準。
你使用推送消息發送的數據必須被加密。這樣可以防止推送服務查看發送的數據。這很重要,因為瀏覽器是可以決定使用哪種推送服務的(它可能使用了一些不受信任且不夠安全的服務器)。
對於每個推送消息,你還可以提供下列說明:
- TTL ——定義消息會在隊列中等多久,超過這個時間消息就會被刪除不做推送。。
- 優先級 ——定義消息的優先級,因為推送服務只發送高優先級的消息,以此來保護用戶設備的電池壽命。
- Topic ——給推送消息一個主題,新消息會替換等待中的帶相同主題的消息,這樣一旦設備處於活動狀態,用戶將不會收到過時的消息。
瀏覽器中的推送事件
如上所述,將消息發送到推送服務後,消息將處於挂機狀態,直到發生下列情況之一:
- 設備上線。
- 消息由於TTL 而在隊列上過期。
當推送服務傳遞消息時,瀏覽器會接收它,解密並在Service Worker中分發一個push
事件。
這裡最好的是,即使是你的網頁沒有打開,瀏覽器也可以執行你的Service Worker。將會發生下面的事情:
- 推送消息到達解密它的瀏覽器
- 瀏覽器喚醒Service Worker
push
事件被分發給Service Worker
設置推送事件監聽器的代碼應該與用JavaScript 編寫的任何其他事件監聽器類似:
self.addEventListener('push', function(event) {
if (event.data) {
console.log('This push event has data: ', event.data.text());
} else {
console.log('This push event has no data.');
}
});
复制代码
需要了解Service Worker 的一點是,你沒有Service Worker 代碼運行時長的控制權。瀏覽器決定何時將其喚醒以及何時終止它。
在Service Workers中,event.waitUntil(promise)
通知瀏覽器工作正在進行,直到promise確定為止,如果它想要完成該工作,它不應該終止sercice worker。
這裡是處理push
事件的例子:
self.addEventListener('push', function(event) {
var promise = self.registration.showNotification('Push notification!');
event.waitUntil(promise);
});
复制代码
調用self.registration.showNotification()
會向用戶發送一個通知,並返回一個promise,只要消息展示了該promise就會觸發resolve。
showNotification(title, options)
方法可以在視覺上進行調整以適應你的需求。title
參數是一個string
,而options是一個看起來像這樣的對象:
{
"//": "Visual Options",
"body": "",
"icon": "",
"image": "",
"badge": "",
"vibrate": "",
"sound": "",
"dir": "",
"//": "Behavioural Options",
"tag": "",
"data": "",
"requireInteraction": "",
"renotify": "",
"silent": "",
"//": "Both Visual & Behavioural Options",
"actions": "",
"//": "Information Option. No visual affect.",
"timestamp": ""
}
复制代码
你可以在這裡閱讀到每個選項內容的更多細節— developer.mozilla.org/en-US/docs/…。
推送通知是一種可以在有緊急、重要和時間敏感的信息需要與用戶進行分享的情況下,吸引用戶注意的絕好方式。